package com.ruoyi.flow.common.tools.mediaconverter.converter.ffmpeg;

import com.ruoyi.flow.common.tools.mediaconverter.converter.command.CommandInvoker;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeoutException;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * 用于设置Ffmpeg位置。 <br/>
 * 有一个默认设置，需要将windows的ffmpeg.exe放到d:/silk2mp3目录下；Linux下的位置会自动寻址。
 */
public class FfmpegLocator {
    private static final Logger LOGGER = LoggerFactory.getLogger(FfmpegLocator.class);
    private String ffmpegDirOnWindows;
    private String ffmpegDirOnLinux;

    private volatile static FfmpegLocator locator = null;

    private FfmpegLocator(String ffmpegDirOnWindows, String ffmpegDirOnLinux) {
        this.ffmpegDirOnWindows = ffmpegDirOnWindows;
        this.ffmpegDirOnLinux = ffmpegDirOnLinux;
    }

    /**
     * 创建默认的FfmpegLocator
     *
     * @return
     */
    public static FfmpegLocator getLocator() {
        if (locator == null) {
            synchronized (FfmpegLocator.class) {
                if (locator == null) {
                    locator = defaultLocator();
                }
            }
        }
        return locator;

    }

    /**
     * 创建自定义的FfmpegLocator
     *
     * @param ffmpegDirOnWindows
     * @param ffmpegDirOnLinux
     * @return
     */
    public static FfmpegLocator getLocator(String ffmpegDirOnWindows, String ffmpegDirOnLinux) {
        if (locator == null) {
            synchronized (FfmpegLocator.class) {
                if (locator == null) {
                    locator = builder().ffmpegDirOnLinux(ffmpegDirOnLinux).ffmpegDirOnWindows(ffmpegDirOnWindows)
                            .create();
                }
            }
        }
        return locator;
    }

    public String getFfmpegDir() {
        if (SystemUtils.IS_OS_LINUX) {
            return this.ffmpegDirOnLinux;
        }
        return this.ffmpegDirOnWindows;
    }

    /**
     * 创建FFMPEG执行程序
     *
     * @return
     * @see FfmpegInvoker
     */
    public FfmpegInvoker createInvoker() {
        return FfmpegInvoker.on(this);
    }

    private static FfmpegLocator defaultLocator() {
        return new Builder().create();
    }

    private static Builder builder() {
        return new Builder();
    }

    private static class Builder {
        private String ffmpegDirOnWindows = "D:/silk2mp3";
        private String ffmpegDirOnLinux = "/usr/local/ffmpeg/bin";

        public Builder ffmpegDirOnWindows(String ffmpegDirOnWindows) {
            checkNotNull(ffmpegDirOnWindows);
            if (SystemUtils.IS_OS_WINDOWS) {
                File ffmpegDir = new File(ffmpegDirOnWindows);
                checkState(ffmpegDir.exists(), "ffmpeg dir {} not exist.", ffmpegDirOnWindows);
            }
            this.ffmpegDirOnWindows = ffmpegDirOnWindows;
            return this;
        }

        public Builder ffmpegDirOnLinux(String ffmpegDirOnLinux) {
            if (!StringUtils.isEmpty(ffmpegDirOnLinux)) {
                if (SystemUtils.IS_OS_LINUX) {
                    File ffmpegDir = new File(ffmpegDirOnLinux);
                    checkState(ffmpegDir.exists(), "ffmpeg dir {} not exist.", ffmpegDirOnLinux);
                }
                this.ffmpegDirOnLinux = ffmpegDirOnLinux;
            }
            return this;
        }

        /**
         * 通过执行"which ffmpeg"命令获取ffmpeg在linux上的位置
         *
         * @return
         */
        public void setDefaultFfmpegDirOnLinux() {
            String command = "which";
            CommandInvoker invoker = new CommandInvoker(command);
            invoker.addArg("ffmpeg");
            try {
                invoker.invoke(3000);
                List<String> input = invoker.getStdInputString();
                String line = input.get(0);
                if (!StringUtils.isEmpty(line)) {
                    this.ffmpegDirOnLinux = StringUtils.substringBeforeLast(line, "/ffmpeg");
                }
            } catch (IOException e) {
                LOGGER.warn("获取ffmpeg在Linux上安装位置出现异常，将采用默认位置{}", this.ffmpegDirOnLinux, e);
            } catch (InterruptedException e) {
                LOGGER.warn("获取ffmpeg在Linux上安装位置出现异常，将采用默认位置{}", this.ffmpegDirOnLinux, e);
            } catch (TimeoutException e) {
                LOGGER.warn("执行ffmepg超时", e);
            } finally {
                invoker.destroy();
            }
        }

        public FfmpegLocator create() {
            if (StringUtils.isEmpty(this.ffmpegDirOnLinux)) {
                setDefaultFfmpegDirOnLinux();
            }
            return new FfmpegLocator(this.ffmpegDirOnWindows, this.ffmpegDirOnLinux);
        }
    }
}
